/*
 * Copyright (C) 2015 Atmel Corporation
 *		      Wenyou.Yang <wenyou.yang@atmel.com>
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <common.h>
#include <clk.h>
#include <dm.h>
#include <malloc.h>
#include <sdhci.h>
//#include <asm/arch/clk.h>
#include <asm/arch/hardware.h>
#include <mmc.h> 




#define FH_SDHC_MIN_FREQ	400000
#define FH_SDHCI_MAX_FREQ  200000000


/**************************EMMC PHY INIT ********************************************/
#define EMMC_PHY_VERSION_ID                 0x00
#define EMMC_PHY_INT_EN                     0x04
#define EMMC_PHY_INT_SRC                    0x08
#define EMMC_PHY_CTRL                       0x0C
#define EMMC_PHY_TEST_O                     0x10
#define EMMC_PHY_TEST_I                     0x14
#define EMMC_PHY_IE_CTRL                    0x18
#define EMMC_PHY_DL_CTRL                    0x1C
#define EMMC_PHY_CTRL2                      0x24

#define BIT_EMMC_DAT_INPUT_EN                                     (0x1 << 31)
#define BIT_EMMC_OUTPUT_BIT_MODE(x)                               (((x) & 0x3) << 29)
#define BIT_EMMC_SAMPLE_EN                                        (0x1 << 28)
#define BIT_EMMC_FIFO_CLR                                         (0x1 << 27)
#define BIT_EMMC_PRBS_GEN_OUT_EN(x)                               (((x) & 0x1FF) << 18)
#define BIT_EMMC_RX_DL_BYPASS_CMD                                 (0x1 << 17)
#define BIT_EMMC_RX_DL_BYPASS_DAT                                 (0x1 << 16)
#define BIT_EMMC_TX_DL_BYPASS                                     (0x1 << 15)
#define BIT_EMMC_RX_CLK_INV_EN_CMD                                (0x1 << 14)
#define BIT_EMMC_RX_CLK_INV_EN_DAT                                (0x1 << 13)
#define BIT_EMMC_RX_CLK_SEL_CMD                                   (0x1 << 12)
#define BIT_EMMC_RX_CLK_SEL_DAT                                   (0x1 << 11)
#define BIT_EMMC_TX_CLK_GATE                                      (0x1 << 10)
#define BIT_EMMC_TX_CLK_INV_EN                                    (0x1 << 9)
#define BITS_EMMC_PRBS_CHK_IN_SEL(X)                              (((x) & 0xF) << 5)
#define BIT_EMMC_PRBS_MODE                                        (0x1 << 4)
#define BIT_EMMC_PRBS_CHK_EN                                      (0x1 << 3)
#define BIT_EMMC_PRBS_GEN_EN                                      (0x1 << 2)
#define BIT_EMMC_LOOPBACK_MODE                                    (0x1 << 1)
#define BIT_EMMC_PHY_MODE                                         (0x1 << 0)

#define BIT_EMMC_CART_RESET_N                                     (0x1 << 9)
#define BITS_EMMC_IE_CTRL_DAT(X)                                  (((x) & 0xFF) << 1)
#define BIT_EMMC_IE_CTRL_CMD                                      (0x1 << 0)

#define BIT_EMMC_TEST_CMD_OE                                      (0x1 << 16)
#define BITS_EMMC_TEST_DAT_OE(x)                                  (((x) & 0xFF) << 17)

#define BIT_EMMC_TEST_CMD_O                                      (0x1 << 0)
#define BITS_EMMC_TEST_DAT_O(x)                                   (((x) & 0xFF) << 1)

/**
* INT_SRC
*/
#define 	EMMC_PHY_MODE		BIT(0)
#define 	LOOPBACK_MODE		BIT(1)
#define 	PRBS_GEN_EN			BIT(2)
#define 	PRBS_CHK_EN			BIT(3)
#define 	PRBS_MODE			BIT(4)
#define 	PRBS_CHK_IN_SEL_OFS	6
#define 	PRBS_CHK_IN_SEL_MSK	(0xf << PRBS_CHK_IN_SEL_OFS)
#define 	TX_CLK_INV_EN		BIT(9)
#define 	TX_CLK_GATE			BIT(10)
#define 	RX_CLK_SEL_DAT		BIT(11)
#define 	RX_CLK_SEL_CMD		BIT(12)
#define 	RX_CLK_INV_EN_DAT	BIT(13)
#define 	RX_CLK_INV_EN_CMD	BIT(14)
#define 	TX_DL_BYPASS		BIT(15)
#define 	RX_DL_BYPASS_DAT	BIT(16)
#define 	RX_DL_BYPASS_CMD	BIT(17)
#define 	PRBS_GEN_OUT_EN_OFS	18
#define 	PRBS_GEN_OUT_EN_MSK	(0x1ff << PRBS_GEN_OUT_EN_OFS)
#define 	FIFO_CLR			BIT(27)
#define 	SAMPLE_EN			BIT(28)
#define 	OUTPUT_BIT_MODE_OFS	29
#define 	OUTPUT_BIT_MODE_MSK	(0x3 << OUTPUT_BIT_MODE_OFS)
#define 	DAT_INPUT_EN		BIT(31)
/**
* IE_CTRL
*/
#define 	IE_CTRL_CMD   		BIT(0)
#define 	IE_CTRL_DAT_OFS		1
#define 	IE_CTRL_DAT_MSK		0xff
#define 	CARD_RESET_N		BIT(9)

#define     TX_SAMPLE_CLK_SEL       BIT(0)
#define     TX_SAMPLE_CLK_X1        BIT(1)
#define     TX_SAMPLE_CLK_X2        BIT(2)
#define		EMMC_CLK_STOP_EN		BIT(3)



extern int gpio_set_value(unsigned gpio, int value);
struct fh_sdhci_phy {
	void *ioaddr;
};

static struct fh_sdhci_phy sdhci_phy;

static inline void sdhci_phy_writel(struct fh_sdhci_phy *host, u32 val, int reg)
{
	writel(val, host->ioaddr + reg);
}

static inline void sdhci_phy_writew(struct fh_sdhci_phy *host, u16 val, int reg)
{
	writew(val, host->ioaddr + reg);
}

static inline void sdhci_phy_writeb(struct fh_sdhci_phy *host, u8 val, int reg)
{
	writeb(val, host->ioaddr + reg);
}
static inline u32 sdhci_phy_readl(struct fh_sdhci_phy *host, int reg)
{
	return readl(host->ioaddr + reg);
}

static inline u16 sdhci_phy_readw(struct fh_sdhci_phy *host, int reg)
{
	return readw(host->ioaddr + reg);
}

static inline u8 sdhci_phy_readb(struct fh_sdhci_phy *host, int reg)
{
	return readb(host->ioaddr + reg);
}
#define REG_EMMC_CLK_RST_CTRL0	(NNP_SYS_APB_REG_BASE + 0x200)
#define REG_EMMC_CLK_RST_CTRL1	(NNP_SYS_APB_REG_BASE + 0x204)

void mmc_ctl_reset(void)
{
	unsigned int cfg;
	cfg = readl(REG_EMMC_CLK_RST_CTRL1);
	cfg |= BIT(1);
	writel(cfg, REG_EMMC_CLK_RST_CTRL1);
	while (readl(REG_EMMC_CLK_RST_CTRL1) & BIT(1))
		;

	cfg = readl(REG_EMMC_CLK_RST_CTRL1);
	cfg |= BIT(0);
	writel(cfg, REG_EMMC_CLK_RST_CTRL1);
	while (readl(REG_EMMC_CLK_RST_CTRL1) & BIT(0))
		;
}

void fh_sdhci_set_clock(struct sdhci_host *host, u32 clock)
{
	static unsigned int emmc_clk[] =
		{24000000, 52000000, 200000000, 400000000};
	unsigned int base_clk = 0, div;
	unsigned int i, cfg;
	struct mmc *mmc = host->mmc;

	for (i = 0; i < ARRAY_SIZE(emmc_clk); i++) {
		if (clock <= emmc_clk[i]) {
			base_clk = emmc_clk[i];
			break;
		}
	}
	if (clock == 200000000) {
		i = 3;
		base_clk = emmc_clk[i];
	}
	cfg = GET_REG(EMMC_PHY_REG_BASE + EMMC_PHY_CTRL);
	cfg &= ~SAMPLE_EN;
	SET_REG((EMMC_PHY_REG_BASE + EMMC_PHY_CTRL), (cfg));

	
	cfg = GET_REG(EMMC_PHY_REG_BASE + EMMC_PHY_CTRL);
	cfg &= ~DAT_INPUT_EN;
	SET_REG((EMMC_PHY_REG_BASE + EMMC_PHY_CTRL), (cfg));
	

	cfg = GET_REG(NNP_SYS_APB_REG_BASE + 0x204);
	cfg &= 0xfffffff3;
	cfg |= (i << 2);
	SET_REG((NNP_SYS_APB_REG_BASE + 0x204), (cfg));

	div = base_clk / clock;
	cfg = GET_REG(NNP_SYS_APB_REG_BASE + 0x200);
	cfg &= 0x80ffffff;
	cfg |= ((div-1) & 0x7f) << 24;
	SET_REG((NNP_SYS_APB_REG_BASE + 0x200), (cfg));

	printf("fh_sdhci_set_clock:%d,%x\n",clock, mmc->card_caps);
	if(clock > 52000000) {
		cfg = GET_REG(EMMC_PHY_REG_BASE + EMMC_PHY_CTRL);
		cfg &= ~((TX_CLK_INV_EN));
		cfg &= ~((RX_CLK_INV_EN_DAT | RX_CLK_INV_EN_CMD));
		SET_REG(EMMC_PHY_REG_BASE + EMMC_PHY_CTRL, cfg);
		if (mmc->timing == MMC_TIMING_MMC_HS400) {
			/*config tx dat dll*/
			cfg = GET_REG(EMMC_PHY_REG_BASE + EMMC_PHY_DL_CTRL);
			cfg |= 0x74;
			SET_REG(EMMC_PHY_REG_BASE + EMMC_PHY_DL_CTRL, cfg);
		} else {
			/*config tx dat dll*/
			cfg = GET_REG(EMMC_PHY_REG_BASE + EMMC_PHY_DL_CTRL);
			cfg |= 0x2d;
			SET_REG(EMMC_PHY_REG_BASE + EMMC_PHY_DL_CTRL, cfg);
		}
		cfg = GET_REG(EMMC_PHY_REG_BASE + EMMC_PHY_CTRL);
		cfg &= (~TX_DL_BYPASS);
		SET_REG(EMMC_PHY_REG_BASE + EMMC_PHY_CTRL, cfg);

		/*config rx dat dll*/
		if (mmc->timing == MMC_TIMING_MMC_HS400) {
			cfg = GET_REG(EMMC_PHY_REG_BASE + EMMC_PHY_DL_CTRL);
			cfg |= (0x40 << 8);
			SET_REG(EMMC_PHY_REG_BASE + EMMC_PHY_DL_CTRL, cfg);
		} else {
			cfg = GET_REG(EMMC_PHY_REG_BASE + EMMC_PHY_DL_CTRL);
			cfg |= (0x44 << 8);
			SET_REG(EMMC_PHY_REG_BASE + EMMC_PHY_DL_CTRL, cfg);
		}
		cfg = GET_REG(EMMC_PHY_REG_BASE + EMMC_PHY_CTRL);
		cfg &= (~RX_DL_BYPASS_DAT);
		SET_REG(EMMC_PHY_REG_BASE + EMMC_PHY_CTRL, cfg);
	
		if (mmc->timing == MMC_TIMING_MMC_HS400) {
		SET_REG(EMMC_PHY_REG_BASE + EMMC_PHY_CTRL2, 0);
		cfg = TX_SAMPLE_CLK_SEL | EMMC_CLK_STOP_EN;
		SET_REG(EMMC_PHY_REG_BASE + EMMC_PHY_CTRL2, cfg);
		cfg |= TX_SAMPLE_CLK_X2;
		SET_REG(EMMC_PHY_REG_BASE + EMMC_PHY_CTRL2, cfg);
	}
#if 0
	if (mmc->timing == MMC_TIMING_MMC_HS400) {
		cfg = GET_REG(EMMC_PHY_REG_BASE + EMMC_PHY_CTRL);
		cfg |= RX_CLK_SEL_DAT;
		SET_REG(EMMC_PHY_REG_BASE + EMMC_PHY_CTRL, cfg);
	}
#endif

	}

	cfg = GET_REG(EMMC_PHY_REG_BASE + EMMC_PHY_CTRL);
	cfg |= FIFO_CLR;
	SET_REG((EMMC_PHY_REG_BASE + EMMC_PHY_CTRL), (cfg));
	cfg &= ~FIFO_CLR;
	SET_REG((EMMC_PHY_REG_BASE + EMMC_PHY_CTRL), (cfg));

	cfg = GET_REG(EMMC_PHY_REG_BASE + EMMC_PHY_CTRL);
	cfg |= DAT_INPUT_EN;
	SET_REG((EMMC_PHY_REG_BASE + EMMC_PHY_CTRL), (cfg));

	cfg = GET_REG(EMMC_PHY_REG_BASE + EMMC_PHY_CTRL);
	cfg |= SAMPLE_EN;
	SET_REG((EMMC_PHY_REG_BASE + EMMC_PHY_CTRL), (cfg));

	host->clock = clock;

}

struct sdhci_ops ops = {
	.set_clock	= fh_sdhci_set_clock,
	.set_ios_post = NULL,
};
#define REG_EMMC_CARD_PAD_SEL	((NNP_SYS_APB_REG_BASE) + 0x208)
int fh_sdhci_phy_init(void)
{
	struct fh_sdhci_phy *fh_phy = &sdhci_phy;
	u32 ctrl;

	fh_phy->ioaddr = (void *)EMMC_PHY_REG_BASE;
	SET_REG_M(0x30100204,1 << 4, 1 << 4);
	ctrl = readl(REG_EMMC_CARD_PAD_SEL);
	ctrl &= (~(0x7 << 8));
	ctrl |= (0x4 << 8);
	writel(ctrl, REG_EMMC_CARD_PAD_SEL);
	
	ctrl = sdhci_phy_readl(fh_phy, EMMC_PHY_CTRL);
	ctrl &= ~(EMMC_PHY_MODE |  RX_CLK_SEL_DAT | RX_CLK_SEL_CMD);
	ctrl |= TX_CLK_GATE | RX_DL_BYPASS_DAT | RX_DL_BYPASS_CMD | TX_DL_BYPASS;
	ctrl |= (RX_CLK_INV_EN_DAT | RX_CLK_INV_EN_CMD);
	ctrl |= (TX_CLK_INV_EN);
	sdhci_phy_writel(fh_phy, ctrl, EMMC_PHY_CTRL);

	ctrl |= FIFO_CLR;
	sdhci_phy_writel(fh_phy, ctrl, EMMC_PHY_CTRL);
	ctrl &= ~FIFO_CLR;
	sdhci_phy_writel(fh_phy, ctrl, EMMC_PHY_CTRL);

	ctrl = sdhci_phy_readl(fh_phy, EMMC_PHY_IE_CTRL);
	ctrl &= ~CARD_RESET_N;
	sdhci_phy_writel(fh_phy, ctrl, EMMC_PHY_IE_CTRL);

	ctrl |= CARD_RESET_N;
	sdhci_phy_writel(fh_phy, ctrl, EMMC_PHY_IE_CTRL);

	ctrl = sdhci_phy_readl(fh_phy, EMMC_PHY_CTRL);
	ctrl |= SAMPLE_EN | DAT_INPUT_EN;
	ctrl &= ~(OUTPUT_BIT_MODE_MSK);
	ctrl |= 3 << OUTPUT_BIT_MODE_OFS;
	sdhci_phy_writel(fh_phy, ctrl, EMMC_PHY_CTRL);

	ctrl = sdhci_phy_readl(fh_phy, EMMC_PHY_CTRL2);
	ctrl |= TX_SAMPLE_CLK_X1;
	sdhci_phy_writel(fh_phy, ctrl, EMMC_PHY_CTRL2);

	ctrl = sdhci_phy_readl(fh_phy, EMMC_PHY_CTRL2);
	ctrl |= EMMC_CLK_STOP_EN;
	sdhci_phy_writel(fh_phy, ctrl, EMMC_PHY_CTRL2);
	return 0;
}
static void print_mmcinfo(struct mmc *mmc)
{
	printf("MMC/SD Card:\n");
	printf("    MID:         0x%x\n", mmc->cid[0] >> 24);
	printf("    Read Block:  %d Bytes\n", mmc->read_bl_len);
	printf("    Write Block: %d Bytes\n", mmc->write_bl_len);
	printf("    Chip Size:   %s Bytes (%s)\n",
			ultohstr(mmc->capacity),
			mmc->high_capacity ? "High Capacity" : "Low Capacity");
	printf("    Name:        \"%c%c%c%c%c\"\n",
			mmc->cid[0] & 0xff,
			(mmc->cid[1] >> 24),
			(mmc->cid[1] >> 16) & 0xff,
			(mmc->cid[1] >> 8) & 0xff,
			mmc->cid[1] & 0xff);

	printf("    Chip Type:   %s\n"
			"    Version:     %d.%d\n",
			IS_SD(mmc) ? "SD" : "MMC",
			EXTRACT_SDMMC_MAJOR_VERSION(mmc->version),
			EXTRACT_SDMMC_MINOR_VERSION(mmc->version));

	printf("    Speed:       %sHz\n", ultohstr(mmc->clock));
	printf("    Bus Width:   %dbit\n", mmc->bus_width);
	printf("    Mode:        %s\n", mmc->strobe_enhanced ? "HS400ES" :
			mmc->timing == MMC_TIMING_MMC_HS400 ? "HS400" :
			mmc->timing == MMC_TIMING_MMC_HS200 ? "HS200" : "HS");
}

int fh_sdhci_int(void)
{
	int ret;
	struct sdhci_host *host = NULL;
	struct mmc *mmc = NULL;
	int dev_num;

	host = calloc(1, sizeof(struct sdhci_host));
	if (!host) {
		puts("sdhci_host malloc fail!\n");
		return -ENOMEM;
	}

	fh_sdhci_phy_init();
	host->name = "fh_sdhci";
	host->ioaddr = (void *)EMMC_REG_BASE;

	host->quirks = SDHCI_QUIRK_32BIT_DMA_ADDR;
	host->ops = &ops;

	host->host_caps = MMC_MODE_HS400 | MMC_MODE_HS200;
	//host->host_caps = MMC_MODE_HS200;
	//host->host_caps = MMC_MODE_HS;
	dev_num = get_mmc_num();
	add_sdhci(host, FH_SDHCI_MAX_FREQ, FH_SDHC_MIN_FREQ);

	mmc = find_mmc_device(dev_num);
	if (!mmc) {
		printf("emmc device not found!!\n");
		return -EINVAL;
	}
	ret = mmc_init(mmc);
	if (ret)
		return ret;

	print_mmcinfo(mmc);
	return 0;
	//return mmc_set_boot_config(mmc);

}

